/**
 * \brief pcm编码成aac
 * \details 输入的pcm文件已经在工程中，生成的test.aac在生成的build-文件夹中
 * \author 将狼才鲸
 * \date 2022-04-02
 * \note FFmpeg版本4.3.1
 * \remarks 代码参考的原地址：
 *          [【FFmpeg】PCM编码成AAC](https://www.jianshu.com/p/2195c757ffff)
 */

#define ENCODE2AAC
//#define ENCODE2MP3 // 找不到编码器

static const char *inFileName =
        "../../004_MultiMedia_VideoAudio/002_pcm_audio_play/002_pcm_audio_play.pcm";

#ifdef ENCODE2AAC
static const char *outFileName = "test.aac";
#else
static const char *outFileName = "test.mp3";
#endif

/**
 * \brief pcm转mp3，方式一
 * \details 只使用编码器AVCodec，不使用输入输出封装结构体AVFormat
 * \note 测试通过，
 *       如果是pcm转mp3则找不到编码器，
 *       当前官方下载的ffmpeg动态库只能编码aac
 */
#if 1
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>       // 编码器
#include <libswresample/swresample.h> // s16转fltp
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>

/* 检测该编码器是否支持该采样格式 */
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
    const enum AVSampleFormat *p = codec->sample_fmts;

    while (*p != AV_SAMPLE_FMT_NONE) { // 通过AV_SAMPLE_FMT_NONE作为结束符
        if (*p == sample_fmt)
            return 1;
        p++;
    }

    return 0;
}

/* 检测该编码器是否支持该采样率 */
static int check_sample_rate(const AVCodec *codec, const int sample_rate)
{
    const int *p = codec->supported_samplerates;
    while (*p != 0) { // 0作为退出条件
        printf("%s support %dhz\n", codec->name, *p);
        if (*p == sample_rate)
            return 1;
        p++;
    }

    return 0;
}

/* 检测该编码器是否支持该采样率, 该函数只是作参考 */
static int check_channel_layout(const AVCodec *codec, const uint64_t channel_layout)
{
    // 不是每个codec都给出支持的channel_layout
    const uint64_t *p = codec->channel_layouts;
    if (!p) {
        printf("the codec %s no set channel_layouts\n", codec->name);
        return 1;
    }
    while (*p != 0) {
        printf("%s support channel_layout %d\n", codec->name, *p);
        if (*p == channel_layout)
            return 1;
        p++;
    }

    return 0;
}

static int check_codec( AVCodec *codec, AVCodecContext *codec_ctx)
{
    if (!check_sample_fmt(codec, codec_ctx->sample_fmt)) {
        fprintf(stderr, "Encoder does not support sample format %s",
                av_get_sample_fmt_name(codec_ctx->sample_fmt));
        return 0;
    }
    if (!check_sample_rate(codec, codec_ctx->sample_rate)) {
        fprintf(stderr, "Encoder does not support sample rate %d", codec_ctx->sample_rate);
        return 0;
    }
    if (!check_channel_layout(codec, codec_ctx->channel_layout)) {
        fprintf(stderr, "Encoder does not support channel layout %lu", codec_ctx->channel_layout);
        return 0;
    }

    printf("Audio encode config\n");
    printf("bit_rate: %ldkbps\n", codec_ctx->bit_rate / 1024);
    printf("sample_rate: %d\n", codec_ctx->sample_rate);
    printf("sample_fmt: %s\n", av_get_sample_fmt_name(codec_ctx->sample_fmt));
    printf("channels: %d\n", codec_ctx->channels);
    // frame_size是在avcodec_open2后进行关联
    printf("one frame_size: %d\n", codec_ctx->frame_size);

    return 1;
}

// 不使用AVFormat时，需要自行生成AAC的头信息，否则播放器无法播放，
// 也无法封装进MP4容器
static void get_adts_header(AVCodecContext *ctx, uint8_t *adts_header, int aac_length)
{
    uint8_t freq_idx = 0; // 0: 96000 Hz, 3: 48000 Hz, 4: 44100 Hz
    switch (ctx->sample_rate) {
        case 96000: freq_idx = 0; break;
        case 88200: freq_idx = 1; break;
        case 64000: freq_idx = 2; break;
        case 48000: freq_idx = 3; break;
        case 44100: freq_idx = 4; break;
        case 32000: freq_idx = 5; break;
        case 24000: freq_idx = 6; break;
        case 22050: freq_idx = 7; break;
        case 16000: freq_idx = 8; break;
        case 12000: freq_idx = 9; break;
        case 11025: freq_idx = 10; break;
        case 8000: freq_idx = 11; break;
        case 7350: freq_idx = 12; break;
        default: freq_idx = 4; break;
    }
    uint8_t chanCfg = ctx->channels;
    uint32_t frame_length = aac_length + 7;
    adts_header[0] = 0xFF;
    adts_header[1] = 0xF1;
    adts_header[2] = ((ctx->profile) << 6) + (freq_idx << 2) + (chanCfg >> 2);
    adts_header[3] = (((chanCfg & 3) << 6) + (frame_length  >> 11));
    adts_header[4] = ((frame_length & 0x7FF) >> 3);
    adts_header[5] = (((frame_length & 7) << 5) + 0x1F);
    adts_header[6] = 0xFC;
}

// 编码一帧
static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output)
{
    int ret;

    /* send the frame for encoding */
    ret = avcodec_send_frame(ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending the frame to the encoder\n");
        return -1;
    }

    /* read all the available output packets (in general there may be any number of them */
    // 编码和解码都是一样的，都是send 1次，然后receive多次, 直到AVERROR(EAGAIN)或者AVERROR_EOF
    while (ret >= 0) {
        ret = avcodec_receive_packet(ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return 0;
        } else if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            return -1;
        }
        uint8_t aac_header[7];
        get_adts_header(ctx, aac_header, pkt->size);

        size_t len = 0;
        len = fwrite(aac_header, 1, 7, output);
        if (len != 7) {
            fprintf(stderr, "fwrite aac_header failed\n");
            return -1;
        }
        len = fwrite(pkt->data, 1, pkt->size, output);
        if (len != pkt->size) {
            fprintf(stderr, "fwrite aac data failed\n");
            return -1;
        }
        /* 是否需要释放数据? avcodec_receive_packet第一个调用的就是 av_packet_unref
        * 所以我们不用手动去释放，这里有个问题，不能将pkt直接插入到队列，因为编码器会释放数据
        * 可以新分配一个pkt, 然后使用av_packet_move_ref转移pkt对应的buffer
        */
        // av_packet_unref(pkt);
    }

    return -1;
}

/**
 * \brief 转换PCM f32le到fltp
 * \details 我电脑的摄像头用不上此函数
 * \note 这里只支持2通道的转换
 * \note S16可以直接使用ffmpeg库函数转换
 */
void f32le_convert_to_fltp(float *f32le, float *fltp, int nb_samples) {
    float *fltp_l = fltp; // 左通道
    float *fltp_r = fltp + nb_samples; // 右通道
    for(int i = 0; i < nb_samples; i++) {
        fltp_l[i] = f32le[i*2];     // 0 1 - 2 3
        fltp_r[i] = f32le[i*2+1];   // 可以尝试注释左声道或者右声道听听声音
    }
}

// 主入口
void pcm2aac(void)
{
#ifdef ENCODE2AAC
    enum AVCodecID codec_id = AV_CODEC_ID_AAC;
#else
    enum AVCodecID codec_id = AV_CODEC_ID_MP3;
#endif

    // 1.查找编码器
    AVCodec *codec = avcodec_find_encoder(codec_id); // 按ID查找则缺省的aac encoder
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    // 2.分配内存
    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }
    codec_ctx->codec_id = codec_id;
    codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
    codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
    codec_ctx->channels       = 2;av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
    codec_ctx->sample_rate    = 44100;
    codec_ctx->bit_rate = 64000;
    //codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
    //codec_ctx->profile = FF_PROFILE_AAC_LOW;

    // 3.检测支持采样格式支持情况
    if (!check_codec(codec, codec_ctx)) {
        exit(1);
    }

    // 4.将编码器上下文和编码器进行关联
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    printf("frame_size: %d\n", codec_ctx->frame_size); // 决定每次到底送多少个采样点

    // 5.打开输入和输出文件
    FILE *infile = fopen(inFileName, "rb");
    if (!infile) {
        fprintf(stderr, "Could not open %s\n", inFileName);
        exit(1);
    }
    FILE *outfile = fopen(outFileName, "wb");
    if (!outfile) {
        fprintf(stderr, "Could not open %s\n", outFileName);
        exit(1);
    }

    // 6.分配packet
    AVPacket *pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "could not allocate the packet\n");
        exit(1);
    }

    // 7.分配frame
    AVFrame *frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate audio frame\n");
        exit(1);
    }

    /* 每次送多少数据给编码器由：
     *  (1)frame_size(每帧单个通道的采样点数);
     *  (2)sample_fmt(采样点格式);
     *  (3)channel_layout(通道布局情况);
     * 3要素决定
     */
    frame->nb_samples     = codec_ctx->frame_size; // default is 1024
    frame->format         = codec_ctx->sample_fmt; // default is FLTP
    frame->channel_layout = codec_ctx->channel_layout;
    // default is 2
    frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);
    frame->sample_rate = codec_ctx->sample_rate;
    printf("frame nb_samples: %d\n", frame->nb_samples);
    printf("frame sample_fmt: %d\n", frame->format);
    printf("frame channel_layout: %lu\n\n", frame->channel_layout);

    // 8.为frame分配buffer
    int ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }

    // 申请转换前的s16帧
    AVFrame *s16_frame = NULL;
    s16_frame = av_frame_alloc();
    if (!s16_frame) {
        printf("av_frame_alloc fail \n");
        exit(1);
    }
    s16_frame->nb_samples = codec_ctx->frame_size;
    s16_frame->format = AV_SAMPLE_FMT_S16;
    s16_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    s16_frame->sample_rate = 44100;
    s16_frame->channels = av_get_channel_layout_nb_channels(s16_frame->channel_layout);
    ret = av_frame_get_buffer(s16_frame, 0);
    if (ret < 0) {
        printf("av_frame_get_buffer fail \n");
        exit(1);
    }

    // 申请转换句柄
    SwrContext *swr_ctx = NULL;
    swr_ctx = swr_alloc_set_opts(NULL, codec_ctx->channel_layout,
                                 codec_ctx->sample_fmt, codec_ctx->sample_rate,
                                 s16_frame->channel_layout, s16_frame->format,
                                 s16_frame->sample_rate, 0, NULL);
    if (!swr_ctx) {
        printf("swr_alloc_set_opts fail \n");
        exit(1);
    }
    ret = swr_init(swr_ctx);
    if (ret < 0) {
        printf("swr_init fail \n");
        exit(1);
    }

    // 9.循环读取数据
    // 计算出每一帧的数据：单个采样点的字节 * 通道数目 * 每帧采样点数量
    // s16的数据量是fltp的一半
    int frame_bytes = av_get_bytes_per_sample(s16_frame->format) \
            * s16_frame->channels \
            * s16_frame->nb_samples;
    printf("frame_bytes %d\n", frame_bytes);
    uint8_t *pcm_buf = (uint8_t *)malloc(frame_bytes);
    if (!pcm_buf) {
        printf("pcm_buf malloc failed\n");
        return;
    }
    uint8_t *pcm_temp_buf = (uint8_t *)malloc(frame_bytes);
    if(!pcm_temp_buf) {
        printf("pcm_temp_buf malloc failed\n");
        return;
    }

    int64_t pts = 0;
    printf("start enode\n");
    for (;;) {
        memset(pcm_buf, 0, frame_bytes);
        size_t read_bytes = fread(pcm_buf, 1, frame_bytes, infile);
        if (read_bytes <= 0) {
            printf("read file finish\n");
            break;
        }

        // 10.确保该frame可写, 如果编码器内部保持了内存参考计数，则需要重新拷贝一个备份，目的是新写入的数据和编码器保存的数据不能产生冲突
        ret = av_frame_make_writable(frame);
        if(ret != 0)
            printf("av_frame_make_writable failed, ret = %d\n", ret);

        // 11.填充音频帧
        if (AV_SAMPLE_FMT_S16 == frame->format) {
            // 如果编码器需要的输入就是AV_SAMPLE_FMT_S16
            // 将读取到的PCM数据填充到frame去，但要注意格式的匹配, 是planar还是packed都要区分清楚
            ret = av_samples_fill_arrays(frame->data, frame->linesize,
                                   pcm_buf, frame->channels,
                                   frame->nb_samples, frame->format, 0);
        } else if (AV_SAMPLE_FMT_FLTP == frame->format) {
            // 如果编码器需要的是AV_SAMPLE_FMT_FLTP
            av_samples_fill_arrays(s16_frame->data, s16_frame->linesize, pcm_buf, s16_frame->channels, s16_frame->nb_samples, s16_frame->format, 0);
            ret = swr_convert(swr_ctx, frame->data, frame->nb_samples, s16_frame->data, s16_frame->nb_samples);

        } else {
            // 如果编码器需要的是AV_SAMPLE_FMT_FLTP，但是摄像头输入的是f32le
            // 将读取到的PCM数据填充到frame去，但要注意格式的匹配, 是planar还是packed都要区分清楚
            // 将本地的f32le packed模式的数据转为float palanar
            memset(pcm_temp_buf, 0, frame_bytes);
            f32le_convert_to_fltp((float *)pcm_buf, (float *)pcm_temp_buf, frame->nb_samples);
            ret = av_samples_fill_arrays(frame->data, frame->linesize,
                                   pcm_temp_buf, frame->channels,
                                   frame->nb_samples, frame->format, 0);
        }

        // 12.编码
        pts += frame->nb_samples;
        frame->pts = pts; // 使用采样率作为pts的单位，具体换算成秒 pts * 1/ 采样率
        ret = encode(codec_ctx, frame, pkt, outfile);
        if (ret < 0) {
            printf("encode failed\n");
            break;
        }
    }

    // 13.刷新编码器
    encode(codec_ctx, NULL, pkt, outfile);

    // 14.关闭文件
    fclose(infile);
    fclose(outfile);

    // 15.释放内存
    if (pcm_buf) {
        free(pcm_buf);
    }
    if (pcm_temp_buf) {
        free(pcm_temp_buf);
    }
    av_frame_free(&frame);
    av_frame_free(&s16_frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);
    printf("main finish, please enter Enter and exit\n");
}
#endif

/**
 * \brief pcm转aac，方式二
 * \details 使用了输入输出封装结构体AVFormat和编码器AVCodec
 * \note aac测试通过，
 *       如果是pcm转mp3则找不到编码器，
 *       当前官方下载的ffmpeg动态库只能编码aac
 */
#if 0
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavcodec/avcodec.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>
#include <stdio.h>
#include <stdlib.h>

/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
    const enum AVSampleFormat *p = codec->sample_fmts;

    while (*p != AV_SAMPLE_FMT_NONE) {
        if (*p == sample_fmt)
            return 1;
        p++;
    }
    return 0;
}

/* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec *codec)
{
    const int *p;
    int best_samplerate = 0;

    if (!codec->supported_samplerates)
        return 44100;

    p = codec->supported_samplerates;
    while (*p) {
        if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))
            best_samplerate = *p;
        p++;
    }
    return best_samplerate;
}

/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec *codec)
{
    const uint64_t *p;
    uint64_t best_ch_layout = 0;
    int best_nb_channels   = 0;

    if (!codec->channel_layouts)
        return AV_CH_LAYOUT_STEREO;

    p = codec->channel_layouts;
    while (*p) {
        int nb_channels = av_get_channel_layout_nb_channels(*p);

        if (nb_channels > best_nb_channels) {
            best_ch_layout   = *p;
            best_nb_channels = nb_channels;
        }
        p++;
    }
    return best_ch_layout;
}

// 编码示例的主入口
void pcm2aac(void)
{
    int ret;
    AVFormatContext *ofmt_ctx = NULL;
    AVCodecContext *dec_ctx = NULL;
    AVCodec *dec = NULL;
    AVPacket *pkt = NULL;
    AVFrame *s16_frame = NULL;
    AVFrame *fltp_frame = NULL;
    SwrContext *swr_ctx = NULL;
    FILE *pcm_f = fopen(inFileName, "rb+");

    ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, outFileName);
    if (ret < 0) {
        printf("avformat_alloc_output_context2 fail \n");
        goto __FAIL;
    }

    dec = avcodec_find_encoder(ofmt_ctx->oformat->audio_codec);
    if (!dec) {
        printf("avcodec_find_encoder fail \n");
        goto __FAIL;
    }

    // AVCodecContext *dec_ctx也可以从输出流中获取
    dec_ctx = avcodec_alloc_context3(dec);
    // AAC编码只支持FLTP PCM输入格式
    dec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
    if (!check_sample_fmt(dec, dec_ctx->sample_fmt)) {
        fprintf(stderr, "Encoder does not support sample format %s",
                av_get_sample_fmt_name(dec_ctx->sample_fmt));
        goto __FAIL;
    }
    dec_ctx->channel_layout = select_channel_layout(dec);
    dec_ctx->channels = av_get_channel_layout_nb_channels(dec_ctx->channel_layout);
    dec_ctx->sample_rate = select_sample_rate(dec);
    dec_ctx->bit_rate = 64000;

    ret = avio_open(&ofmt_ctx->pb, outFileName, AVIO_FLAG_WRITE);
    if (ret < 0) {
        printf("avio_open fail \n");
        goto __FAIL;
    }
    ret = avcodec_open2(dec_ctx, dec, NULL);
    if (ret < 0) {
        printf("avcodec_open2 fail \n");
        goto __FAIL;
    }
    AVStream *st = avformat_new_stream(ofmt_ctx, dec);
    ret = avcodec_parameters_from_context(st->codecpar, dec_ctx);
    if (ret < 0) {
        printf("avcodec_parameters_from_context fail \n");
        goto __FAIL;
    }

    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf("avformat_write_header fail \n");
        goto __FAIL;
    }

    s16_frame = av_frame_alloc();
    if (!s16_frame) {
        printf("av_frame_alloc fail \n");
        goto __FAIL;
    }
    s16_frame->nb_samples = dec_ctx->frame_size;
    s16_frame->format = AV_SAMPLE_FMT_S16;
    s16_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    s16_frame->sample_rate = 44100;
    //s16_frame->channels = av_get_channel_layout_nb_channels(s16_frame->channel_layout);
    ret = av_frame_get_buffer(s16_frame, 0);
    if (ret < 0) {
        printf("av_frame_get_buffer fail \n");
        goto __FAIL;
    }
    pkt = av_packet_alloc();
    if (!pkt) {
        printf("av_packet_alloc fail \n");
        goto __FAIL;
    }
    int pts_i = 0;

    swr_ctx = swr_alloc_set_opts(NULL, dec_ctx->channel_layout, dec_ctx->sample_fmt, dec_ctx->sample_rate, s16_frame->channel_layout, s16_frame->format, s16_frame->sample_rate, 0, NULL);
    if (!swr_ctx) {
        printf("swr_alloc_set_opts fail \n");
        goto __FAIL;
    }
    ret = swr_init(swr_ctx);
    if (ret < 0) {
        printf("swr_init fail \n");
        goto __FAIL;
    }
    fltp_frame = av_frame_alloc();
    fltp_frame->nb_samples = dec_ctx->frame_size;
    fltp_frame->format = dec_ctx->sample_fmt;
    fltp_frame->channel_layout = dec_ctx->channel_layout;
    fltp_frame->sample_rate = dec_ctx->sample_rate;
    //fltp_frame->channels = av_get_channel_layout_nb_channels(s16_frame->channel_layout);
    ret = av_frame_get_buffer(fltp_frame, 0);
    if (ret < 0) {
        printf("av_frame_get_buffer fail \n");
        goto __FAIL;
    }
    uint64_t pcm_buffer_size = s16_frame->nb_samples*av_get_bytes_per_sample(s16_frame->format)*s16_frame->channels;
    uint8_t *pcm_buffer = av_malloc(pcm_buffer_size);

    while (feof(pcm_f) == 0) {
        size_t size = fread(pcm_buffer, 1, pcm_buffer_size, pcm_f);
        int nb_samples = size/(av_get_bytes_per_sample(s16_frame->format)*s16_frame->channels);
        s16_frame->nb_samples = nb_samples;
        fltp_frame->nb_samples = nb_samples;

        av_samples_fill_arrays(s16_frame->data, s16_frame->linesize, pcm_buffer, s16_frame->channels, s16_frame->nb_samples, s16_frame->format, 0);
        ret = swr_convert(swr_ctx, fltp_frame->data, fltp_frame->nb_samples, s16_frame->data, s16_frame->nb_samples);
        if (size == 0) {
            printf("fread fail \n");
            break;
        }
        pts_i += fltp_frame->nb_samples;
        fltp_frame->pts = pts_i;
        ret = avcodec_send_frame(dec_ctx, fltp_frame);
        if (ret < 0) {
            printf("avcodec_send_frame fail \n");
            break;
        }
        while (1) {
            ret = avcodec_receive_packet(dec_ctx, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                printf("avcodec_receive_packet fail \n");
                break;
            }
            ret = av_interleaved_write_frame(ofmt_ctx, pkt);
            if (ret < 0) {
                printf("av_interleaved_write_frame fail \n");
                break;
            }
            av_packet_unref(pkt);
        }
    }
    ret = avcodec_send_frame(dec_ctx, NULL);
    if (ret < 0) {
        printf("avcodec_send_frame fail \n");
        goto __FAIL;
    }
    while (1) {
        ret = avcodec_receive_packet(dec_ctx, pkt);
        if (ret == AVERROR(EINVAL) || ret == AVERROR_EOF) {
            break;
        } else if (ret < 0) {
            printf("avcodec_receive_packet fail \n");
            break;
        }
        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
        if (ret < 0) {
            printf("av_interleaved_write_frame fail \n");
            break;
        }
        av_packet_unref(pkt);
    }
    ret = av_write_trailer(ofmt_ctx);
    if (ret < 0) {
        printf("av_write_trailer fail \n");
    }

__FAIL:
    if (ofmt_ctx->pb)
        avio_close(ofmt_ctx->pb);
    if (dec_ctx)
        avcodec_close(dec_ctx);
    if (pcm_buffer)
        av_free(pcm_buffer);
    if (ofmt_ctx)
        avformat_free_context(ofmt_ctx);
    if (s16_frame)
        av_frame_free(&s16_frame);
    if (fltp_frame)
        av_frame_free(&fltp_frame);
    if (pkt)
        av_packet_free(&pkt);
}
#endif

/**
 * \brief pcm转mp3，方式二
 * \details 使用了输入输出封装结构体AVFormat和编码器AVCodec
 * \note 这里要求原始PCM输入直接就是FLTP格式，而不是S16格式，
 *       这样不需要重采样转换输入的PCM格式，
 *       在我的电脑上测试不通过，因为我电脑摄像头输入的是S16的格式
 */
#if 0
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>

void encode(AVCodecContext* codecCtx, AVFrame* frame, AVPacket* packet, AVFormatContext* formatCtx);
static int ptsIndex = 0;
void pcm2aac(void)
{
    int ret = 0;
    AVFormatContext* formatCtx = NULL;
    AVStream* stream = NULL;
    AVCodecContext* codecCtx = NULL;
    AVCodec* codec = NULL;
    AVFrame* frame = NULL;
    AVPacket* packet = NULL;

    /* 根据传入的音频类型ID寻找并返回对应解码器 */
#ifdef ENCODE2AAC
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
#else
    codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
#endif
    if (!codec)
    {
        printf("codec not find\n");
        exit(1);
    }

    /* 给编解码上下文分配内存并赋默认值  */
    codecCtx = avcodec_alloc_context3(codec);
    if (!codecCtx)
    {
        printf("Could not allocate audio codec context\n");
        exit(1);
    }

    /* 设置码率，大概为62kb/s */
    codecCtx->bit_rate = 64000;
    /* 采样格式, 类似于图像存储格式，位数越多，表示值越精确 */
    codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;

    /*  检查编码器是否支持这种采样格式 */
    const enum AVSampleFormat* sampleFmt = codec->sample_fmts;

    while (*sampleFmt != AV_SAMPLE_FMT_NONE)
    {
        if (*sampleFmt == codecCtx->sample_fmt)
            break;
        sampleFmt++;
    }
    if (*sampleFmt != codecCtx->sample_fmt)
    {
        printf("Encoder not support sample format,%d %d\n",*sampleFmt,codecCtx->sample_fmt);
        exit(1);
    }

    /* 采样率：采样设备每秒抽取样本的次数；下面操作是选择最高的采样率 */
    if (!codec->supported_samplerates)
        codecCtx->sample_rate = 44100;

    const int* tempPtr = codec->supported_samplerates;
    int bestSampleRate = 0;
    while (*tempPtr)
    {
        if (!bestSampleRate || abs(44100 - *tempPtr) < abs(44100 - bestSampleRate))
            bestSampleRate = *tempPtr;
        tempPtr++;
    }
    codecCtx->sample_rate = bestSampleRate;

    /* 通道布局：是一个64位整数，每个值为1的位对应一个通道；
     下面操作从编码器中选择具有最高通道计数的布局 */
    if (!codec->channel_layouts)
        codecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
    else
    {
        const uint64_t* tempPtr2 = codec->channel_layouts;
        int bestNbChannel = 0;
        uint64_t bestChLayout = 0;
        while (*tempPtr2)
        {
            int nbChannel = av_get_channel_layout_nb_channels(*tempPtr2);
            if (nbChannel > bestNbChannel)
            {
                bestChLayout = *tempPtr2;
                bestNbChannel = nbChannel;
            }
            tempPtr2++;
        }
        codecCtx->channel_layout = bestChLayout;
    }

    /* 具体通道个数 */
    codecCtx->channels = av_get_channel_layout_nb_channels(codecCtx->channel_layout);

    /* 初始化AVCodecContext，各种分配内存和检查，初始化到具体的编码器库 */
    ret = avcodec_open2(codecCtx, codec, NULL);
    if (ret != 0)
    {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    /* 新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP，老版本的是AV_SAMPLE_FMT_S16
     如果输入的PCM数据是AV_SAMPLE_FMT_S16的，avcodec_encode_audio2会返回-22错误 */
    FILE* inFile = fopen(inFileName, "rb");

    packet = av_packet_alloc();
    frame = av_frame_alloc();

    frame->nb_samples = codecCtx->frame_size;
    frame->format = codecCtx->sample_fmt;
    frame->channel_layout = codecCtx->channel_layout;

    ret = av_frame_get_buffer(frame,0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }

    /* 分配内存，根据格式名(第三个参数)或者文件名(第四个参数)获取输出格式 */
    avformat_alloc_output_context2(&formatCtx, NULL, NULL, outFileName);
    if (!formatCtx)
    {
        printf("could not alloc output context,exit!!\n");
        exit(1);
    }

    stream = avformat_new_stream(formatCtx, codec);
    if (avcodec_parameters_from_context(stream->codecpar, codecCtx) < 0)
    {
        fprintf(stderr,"copy parameters fail!\n");
        exit(1);
    }

    /* AVFMT_NOFILE跟是否是一个真实的文件无关 */
    if (!(formatCtx->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open2(&formatCtx->pb, outFileName, AVIO_FLAG_WRITE, NULL, NULL))
        {
            printf("avio open fail\n");
            exit(1);
        }
    }

    /* 每个通道占的字节数 */
    int dataSize = av_get_bytes_per_sample(codecCtx->sample_fmt);

    /*  针对不同的编码格式，写不同的文件头*/
    if (avformat_write_header(formatCtx, NULL) < 0)
    {
        fprintf(stderr, "Error occurred when opening output file: %s\n",av_err2str(ret));
        goto end;
    }

    while (!feof(inFile))
    {
         /* 确保frame是可写的，例如编码器要缓存此帧，下面函数就会分配新的缓冲区，并复制参数 */
        ret = av_frame_make_writable(frame);
        if (ret < 0)
        {
            printf("%s %d ret:%d\n",__FUNCTION__,__LINE__,ret);
            goto end;
        }

        for (int i = 0; i < codecCtx->frame_size; ++i)
            for (int j = 0; j < codecCtx->channels; ++j)
                fread(frame->data[j]+dataSize * i, 1, dataSize, inFile);

        encode(codecCtx,frame,packet,formatCtx);
    }

    /* 发送一个空帧，可以防止编码器中数据未处理完 */
    encode(codecCtx, NULL, packet, formatCtx);

    /* 针对不同的编码格式，写不同的文件尾 */
    if (av_write_trailer(formatCtx) != 0)
        printf("write file trail fail\n");

    printf("%s %d  end!!!\n",__FUNCTION__,__LINE__);

end:
    if(inFile)
        fclose(inFile);
    avcodec_free_context(&codecCtx);
    avformat_free_context(formatCtx);
    av_frame_free(&frame);
    av_packet_free(&packet);
}

void encode(AVCodecContext* codecCtx, AVFrame* frame,
            AVPacket* packet, AVFormatContext* formatCtx)
{
    int ret = 0;

    ret = avcodec_send_frame(codecCtx, frame);
    if (ret < 0)
    {
        printf(" send frame to encodec faile! ret:%d\n",ret);
        exit(1) ;
    }

    while (ret >= 0)
    {
        ret = avcodec_receive_packet(codecCtx,packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error encoding audio frame\n");
            exit(1);
        }

        packet->pts = ptsIndex*100;
        packet->dts = ptsIndex*100;
        packet->pos = -1;
        packet->stream_index = 0;

        if (av_interleaved_write_frame(formatCtx,packet) != 0)
        {
            printf("write frame fail\n");
            break;
        }

        ptsIndex++;

        av_packet_unref(packet);
    }
}
#endif

/**
 * \brief 使用了AVFormat和AVCodec，例子3
 * \note 测试通过
 */
#if 0
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>

// 写入最后一帧
int flush_encoder(AVFormatContext *ofmt_ctx, int stream_index)
{
  if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities & AV_CODEC_CAP_DELAY)) {
    return 0;
  }

  int got_fame = 0;
  AVPacket *pkt = av_packet_alloc();

  while (1)
  {
    pkt->data = NULL;
    pkt->size = 0;
    av_init_packet(pkt);
    int ret = avcodec_encode_audio2(ofmt_ctx->streams[stream_index]->codec, pkt, NULL, &got_fame);
    if (ret < 0) {
      break;
    }
    if (got_fame == 0) {
      break;
    }
    ret = av_write_frame(ofmt_ctx,pkt);
    if (ret < 0) {
      break;
    }
  }
  av_packet_free(&pkt);
  return 0;
}

// 主入口
void pcm2aac(void)
{
  av_register_all(); // 注册所有封装
  AVFormatContext *ofmt_ctx = avformat_alloc_context();
  AVOutputFormat *oformat = av_guess_format(NULL, outFileName, NULL);
  if (oformat == NULL) {
    av_log(NULL,AV_LOG_ERROR, "fail to find the output format\n");
    return;
  }
  if (avformat_alloc_output_context2(&ofmt_ctx, oformat, oformat->name, outFileName) <0) {
    av_log(NULL,AV_LOG_ERROR, "fail to alloc output context\n");
    return;
  }
  AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
  if (out_stream == NULL) {
    av_log(NULL,AV_LOG_ERROR, "fail to create new stream\n");
    return;
  }
  AVCodecContext *pCodecCtx = out_stream->codec;
  pCodecCtx->codec_id = oformat->audio_codec; // 如果要使用硬件解码器，就不能按id找，要按名称找
  pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
  pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP; // 其他会出错
  pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
  pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
  pCodecCtx->sample_rate = 44100;
  pCodecCtx->bit_rate = 128000;

  AVCodec *pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
  if (pCodec == NULL) {
    av_log(NULL,AV_LOG_ERROR, "fail to find codec\n");
    return;
  }
  if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
    av_log(NULL,AV_LOG_ERROR, "fail to open codec\n");
    return;
  }
  av_dump_format(ofmt_ctx, 0, outFileName, 1);
  if (avio_open(&ofmt_ctx->pb, outFileName, AVIO_FLAG_WRITE) < 0) {
    av_log(NULL,AV_LOG_ERROR, "fail to open output\n");
    return;
  }
  if (avformat_write_header(ofmt_ctx, NULL) < 0) {
    av_log(NULL, AV_LOG_ERROR, "fail to write header");
    return;
  }
  FILE *fp = fopen(inFileName, "rb");
  if (fp == NULL) {
    printf("fail to open file\n");
    return;
  }
  AVFrame *pframe = av_frame_alloc();
  pframe->channels = pCodecCtx->channels;
  pframe->format = pCodecCtx->sample_fmt;
  pframe->nb_samples = pCodecCtx->frame_size;

  int size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
  uint8_t *out_buffer = (uint8_t*)av_malloc(size);
  avcodec_fill_audio_frame(pframe, pCodecCtx->channels, pCodecCtx->sample_fmt, (const uint8_t *)out_buffer, size, 1);

  // 新版本需要使用到转换参数，将读取的数据转换成输出的编码格式
  uint8_t **data = (uint8_t **)av_calloc(pCodecCtx->channels, sizeof(*data));
  av_samples_alloc(data, NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);

  SwrContext *pSwrCtx  = swr_alloc();
  swr_alloc_set_opts(pSwrCtx, pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate,
      pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16, 44100, 0, NULL);
  swr_init(pSwrCtx);
  AVPacket *pkt = av_packet_alloc();
  av_new_packet(pkt,size);
  pkt->data = NULL;
  pkt->size = 0;

  int count = 1;
  /* 编码每一帧 */
  while (1) {
    // 读取的长度和原始数据的采样率、采样格式以及通道数有关，如果size设置的不对，会导致音频错误
    size = pframe->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * pframe->channels;
    if (fread(out_buffer, 1, size, fp) < 0) {
      printf("fail to read raw data\n");
      return;
    } else if (feof(fp)) {
      break;
    }

    swr_convert(pSwrCtx, data, pCodecCtx->frame_size, pframe->data, pframe->nb_samples);
    // 转换后的数据大小与采样率和采样格式有关
    size = pCodecCtx->frame_size * av_get_bytes_per_sample(pCodecCtx->sample_fmt);
    memcpy(pframe->data[0], data[0], size);
    memcpy(pframe->data[1], data[1], size);
    pframe->pts = count * 100;
    // 编码写入
    if (avcodec_send_frame(pCodecCtx, pframe) < 0){
      printf("fail to send frame\n");
      return;
    }
    // 读取编码好的数据
    if (avcodec_receive_packet(pCodecCtx, pkt) >= 0) {
      pkt->stream_index = out_stream->index;
      av_log(NULL, AV_LOG_INFO, "write %d frame\n", count);
      av_write_frame(ofmt_ctx, pkt);
    }
    count++;
    av_packet_unref(pkt);
  }

  // 刷新编码器的缓冲区
  flush_encoder(ofmt_ctx, out_stream->index);

  av_packet_free(&pkt);
  swr_free(&pSwrCtx);
  av_free(out_buffer);
  av_frame_free(&pframe);
  av_write_trailer(ofmt_ctx);
  avio_close(ofmt_ctx->pb);
  avformat_free_context(ofmt_ctx);
}
#endif
